﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using com.yoozoo.gta.Gameplay.RTS;
using com.youzu.warh.protocol;
using ProtoBuf;
using UnityEngine;
using UnityEngine.Networking;
using Yoozoo.External.LZ4;
using Yoozoo.Libs;

namespace Yoozoo.Gameplay.RTS
{
    public class ReportRequester
    {
        const int REVIVE_BUFFER_LEN = 128 * 1024;

        byte[] reciveBuffer = new byte[REVIVE_BUFFER_LEN];
        MemoryStream msRecive;

        private long requestReportStartTime;
        private bool isRequestingReport;
        private bool isRequestReportTimeout;

        private string reportUrl;

        private const int MAX_AGAIN_TIMES = 3;
        private const long AGAIN_INTERVAL = 1000;
        private const long NO_RESPONSE_TIME_OUT = 8000;
        private int requestAgainTimes;
        private long requestAgainStartTime;
        private bool needRequestAgain;

        public delegate object ReportDeserializeHandler(byte[] data);
        public delegate void ReportReadyHandler(object data);
        public delegate void ReportFailedHandler(bool timeout);

        private ReportDeserializeHandler deserializeHandler;
        private ReportFailedHandler onFailedHandler;
        private ReportReadyHandler onReadyHandler;
        
        public void Initialize()
        {
            msRecive = new MemoryStream(reciveBuffer, 0, REVIVE_BUFFER_LEN, true, true);
        }

        public void Restart()
        {
            requestAgainTimes = 0;
        }

        private bool RequestAgain()
        {
            if (requestAgainTimes >= MAX_AGAIN_TIMES)
            {
                return false;
            }

            requestAgainStartTime = TimeUtils.GetServerTimeMs();
            requestAgainTimes++;
            needRequestAgain = true;
            return true;
        }

        public void SetHandler(ReportDeserializeHandler deserializeHandler, ReportReadyHandler onReadyHandler,
            ReportFailedHandler onFailedHandler)
        {
            this.deserializeHandler = deserializeHandler;
            this.onFailedHandler = onFailedHandler;
            this.onReadyHandler = onReadyHandler;
        }
        
        
        public void RequestReport(string url)
        {
            RequestReportInternal(url);
        }

        private void RequestReportInternal(string url)
        {
            reportUrl = url;
            UnityWebRequest www = UnityWebRequest.Get(url);
            var request = www.SendWebRequest();
            request.completed += OnRequest;
            isRequestingReport = true;
            requestReportStartTime = TimeUtils.GetServerTimeMs();
            isRequestReportTimeout = false;
        }

        public void Update()
        {
            if (isRequestingReport)
            {
                if (TimeUtils.GetServerTimeMs() - requestReportStartTime > NO_RESPONSE_TIME_OUT)
                {
                    if (Debug.isDebugBuild)
                    {
                        Debug.LogError("[RTS竞技场]战报请求超时");
                    }
                    isRequestingReport = false;
                    isRequestReportTimeout = true;
                    OnGetReportFailed(true);
                }
            }
            else if (needRequestAgain)
            {
                if (TimeUtils.GetServerTimeMs() - requestAgainStartTime > AGAIN_INTERVAL)
                {
                    needRequestAgain = false;
                    if (Debug.isDebugBuild)
                    {
                        Debug.LogError("[RTS竞技场]开始重新请求:" + reportUrl);
                    }
                    RequestReportInternal(reportUrl);
                }
            }
        }

        private void OnGetReportFailed(bool timeout = false)
        {
            onFailedHandler?.Invoke(timeout);
        }
        
        private void OnRequest(AsyncOperation obj)
        {
            isRequestingReport = false;
            if (isRequestReportTimeout)
                return;
            var async = obj as UnityWebRequestAsyncOperation;
            var www = async.webRequest;
            if (www.isNetworkError)
            {
                Debug.LogError("battleReportResponse is isNetworkError: " + www.error + "    " + www.url);
                OnGetReportFailed();
            }
            else
            {
                byte[] result = www.downloadHandler.data;
                OnGetReport(result);
            }

            isRequestReportTimeout = false;
        }

        private void OnGetReport(byte[] rawData)
        {
            //序列化
            var data = deserializeHandler(rawData);
            if (data == null)
            {
                if (!RequestAgain())
                {
                    Debug.LogError("战报序列化失败");
                    OnGetReportFailed();
                }
            }
            else
            {
                needRequestAgain = false;
                onReadyHandler(data);
            }
        }
        
        
        [Serializable]
        private class ReportFile
        {
            public string data;
            public int status;
        }
        
        public ArenaBattleReport DeserializeArenaBattleReport(byte[] message)
        {
            if (Debug.isDebugBuild)
            {
                Debug.Log("[RTS竞技场]战报长度："+(message.Length / 1024).ToString("0.0")+" kb");
            }
            var jsonString = Encoding.UTF8.GetString(message);
            var reportFile = JsonUtility.FromJson<ReportFile>(jsonString);
            if (reportFile == null)
            {
                Debug.LogError("[RTS竞技场]战报Json解析错误");
                return null;
            }

            if (reportFile.status != 0)
            {
                Debug.LogError("[RTS竞技场]战报请求出错，status = " + reportFile.status);
                return null;
            }

            if (reportFile.data == null)
            {
                Debug.LogError("[RTS竞技场]战报data为空："+jsonString);
                return null;
            }
            var data = Convert.FromBase64String(reportFile.data);
            try
            {
                int decodeLen = Converter.GetBigEndian(BitConverter.ToInt32(data, 0));
                var msgLen = data.Length - 4;
                byte[] proto = null;
                if (decodeLen == 0)
                {
                    int protoLen = msgLen;
                    proto = new byte[protoLen];
                    Array.Copy(data, 4, proto, 0, protoLen);
                }
                else
                {
                    int decodeLength = msgLen;
                    byte[] outPutBytes = new byte[decodeLen];
                    if (LZ4Codec.Decode(data, 4, decodeLength, outPutBytes, 0,
                            decodeLen) == decodeLen)
                    {
                        proto = outPutBytes;
                    }
                    else
                    {
                        UnityEngine.Debug.LogErrorFormat("<color=red> LZ4Codec.Decode failed!!!</color>");
                    }
                }

                if (proto != null)
                {
                    using (MemoryStream ms = new MemoryStream(proto))
                    {
                    
                        ArenaBattleReport report = ProtoBufSerializer.Deserialize(ms,typeof(ArenaBattleReport),proto.Length) as ArenaBattleReport;
                        if (report != null)
                        {
                            needRequestAgain = false;
                        }
                        else
                        {
                            Debug.LogError("[RTS竞技场]战报Proto解析错误:\n"+reportFile.data);
                        }
#if UNITY_EDITOR
                        /*FileStream fs = new FileStream(System.Environment.CurrentDirectory  + "/Caches/" + reportId + ".txt", FileMode.Create);
                        fs.Write(Encoding.UTF8.GetBytes(jsonString), 0, jsonString.Length);
                        fs.Close();*/
#endif
                        return report;
                    }
                }
            }
            catch (Exception e)
            {
                Debug.LogError("[RTS竞技场]战报Proto解析过程中报错:\n"+reportFile.data);
                Debug.LogError(e.ToString());
#if UNITY_EDITOR
                /*FileStream fs = new FileStream(System.Environment.CurrentDirectory  + "/Caches/ERROR_" + reportId + ".txt", FileMode.Create);
                fs.Write(Encoding.UTF8.GetBytes(jsonString), 0, jsonString.Length);
                fs.Close();*/
#endif
            }
            return null;
        }

    }
}
